// exe_overlap.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <numeric>
#include <cmath>
#include <algorithm>

#include <boost/date_time/posix_time/posix_time.hpp>

#include <quigon.h>

#ifdef MACRO_STK
#include "stk_para.h"
#include "stk_lidar.h"
#include "stk_lidar_io.h"
#endif	//MACRO_STK

#ifdef MACRO_YL
#include "stk_para.h"
#include "yl_lidar.h"
#include "yl_lidar_io.h"
#endif //MACRO_YL

#include "basic_process.h"
#include "noise_filter.h"
#include "fernald.h"
#include "output_lidarpro.h"


using namespace std;
using namespace boost::posix_time;
using namespace quigon;

#ifdef MACRO_STK
typedef STK_Para Para;
typedef STK_Line Line;
const string para_file = "../settings/stk_lidar_para.txt";
const string prefix = "stk";
const vector<string> &aver_attr = stk_aver_attr;
#endif	//MACRO_STK

#ifdef MACRO_YL
typedef YL_Para Para;
typedef YL_Line Line; 
const string para_file = "../settings/yl_lidar_para.txt";
const string prefix = "yl";
const vector<string> &aver_attr = yl_aver_attr;
#endif	//MACRO_YL

//overlap start_t, end_t, aod, low_dist, high_dist
int main(int argc, char* argv[])
{
	string start_t, end_t;
	double aod;
	double low_dist, high_dist;
	if (argc < 5)
	{
		cout << "usage: ***_overlap yyyymmddThhmmss yyyymmddThhmmss low_h high_h" << endl;
		cout << "The capital 'T' in the 'iso time string' is optional" << endl;
		exit(0);
	}
	else
	{
		start_t = argv[1];
		end_t = argv[2];
		low_dist = atof(argv[3]);
		high_dist = atof(argv[4]);
	}

	// Check the date_time format, if not iso string, add 'T' into it.
	if (start_t[8] != 'T')
		start_t.insert(8, 1, 'T');
	if (end_t[8] != 'T')
		end_t.insert(8, 1, 'T');

	time_duration aver_duration = from_iso_string(end_t) - from_iso_string(start_t);
	double aver_seconds = aver_duration.total_milliseconds() / 1000.0;
	// Load lidar parameters
	Para para(para_file);

	size_t low_index = para.distance_i[low_dist];
	size_t high_index = para.distance_i[high_dist];

	// Making a container for the data
	list<Line> container;

	// import the data.
	cout << "Importing data..." << endl;
	import(
	        container,
	        from_iso_string(start_t),       // boost::posix_time::from_iso_string
	        from_iso_string(end_t),     // take care of the "T" in the string
	        para
	);

	cout << "lines num: " << container.size() << endl;

	cout << "Making an averager" << endl;
	Average_Machine<Line> aver(aver_seconds, aver_attr);
	// Make a functor for correction, and more options are available
	// e.g. Correction<STK_Line> my_corr(para, Status::BACKGROUND | Status::AFTERPULSE | Status::DISTANCE);
	// default is doing every possible correction
	cout << "Making a corrector" << endl;
	Correction<Line, Para> corr(para, Status::BACKGROUND | Status::AFTERPULSE | Status::DISTANCE , false);

	// Make a filter
//    cout << "Making a filter ..." << endl;
//    Serie<string, double> filter_para("../settings/lidar/stk_filter.txt");
//    Noise_Filter_Proto filter(
//        filter_para["low_index"],
//        filter_para["high_index"],
//        filter_para["low_buddy_num"],
//        filter_para["high_buddy_num"],
//        para.zero_meter_index,
//        para.size - 1);
//    double trigger_value = filter_para["trigger_value"];
//    size_t lowest_trigger_index = filter_para["lowest_trigger_index"];

	aver(container);
	Line the_line = container.front();
	corr(the_line);
//	filter(the_line, trigger_value, lowest_trigger_index);
	kick_minus(the_line, 0.000001);  // for log
	for (Line::iterator iter = the_line.begin(); iter != the_line.end(); ++iter)
	{
		*iter = log(*iter);
	}

	size_t linear_size = high_index - low_index + 1;
	vector<double> dist(linear_size);
	vector<double> log_signal(linear_size);
	for (size_t i = low_index, j = 0; i <= high_index; ++i, ++j)
	{
		dist[j] = para.distance[i];
		log_signal[j] = the_line[i];
	}
	boost::tuple<double, double, double> linear_result = linear_fit(dist, log_signal);
	double a = linear_result.get<0>();
	double b = linear_result.get<1>();
	double r = linear_result.get<2>();

	cout << "y = a + bx, a = " << a << ", b = " << b << endl;
	cout << "r = " << r << endl;
	cout << "Visibility is about : " << 3.91 * 2.0 / fabs(b) << " km." << endl;

	Lidar_Profile overlap;
	overlap["bin_size"] = the_line["bin_size"];
	overlap["start_value"] = para.start_value;
	overlap.resize(low_index + 1);
	for (size_t i = 0; i <= low_index; ++i)
	{
		if (i < para.zero_meter_index)
			overlap[i] = 0;
		else
		{
			overlap[i] = 1.0 / exp(the_line[i] - a - b * para.distance[i]);
		}
	}

	Serie<double, double> detail;
	for (size_t i = para.zero_meter_index; i < the_line.size(); ++i)
	{
		detail.add_entry(para.distance[i], the_line[i]);
	}

	string filename = string("../settings/calibration/") + prefix + "_" + start_t + "_" + end_t + ".overlap.txt";
	string detail_filename = string("../settings/calibration/") + prefix + "_" + start_t + "_" + end_t + ".overlap_detail.txt";
	stringstream ss;
	ss << "y = a + bx, a = " << a << ", b = " << b << ", r = " << r ;
	string note;
	getline(ss, note);
	overlap.save(filename, note);
	detail.save(detail_filename, note);

	return 0;
}


